use std::collections::HashMap;

// 请求方法
#[derive(Debug, PartialEq)]
pub enum Method {
    Get,
    Post,
    Uninitialized,
}

// 请求方法转换
impl From<&str> for Method {
    fn from(s: &str) -> Method {
        match s {
            "GET" => Method::Get,
            "POST" => Method::Post,
            _ => Method::Uninitialized,
        }
    }
}

// 请求版本
#[derive(Debug, PartialEq)]
pub enum Version {
    V1_1,
    V2_0,
    Uninitialized,
}

// 请求版本转换
impl From<&str> for Version {
    fn from(s: &str) -> Version {
        match s {
            "HTTP/1.1" => Version::V1_1,
            _ => Version::Uninitialized,
        }
    }
}

// 请求资源
#[derive(Debug, PartialEq)]
pub enum Resource {
    Path(String),
}

#[derive(Debug)]
pub struct HttpRequest {
    pub method: Method,
    pub version: Version,
    pub resource: Resource,
    pub headers: HashMap<String, String>,
    pub msg_body: String,
}
fn pricess_req_line(req: &str) -> (Method, Resource, Version) {
    let mut req_line = req.split_whitespace();
    let method = req_line.next().unwrap();
    let resource = req_line.next().unwrap();
    let version = req_line.next().unwrap();
    (
        method.into(),
        Resource::Path(resource.to_string()),
        version.into(),
    )
}

fn process_header_line(line: &str) -> (String, String) {
    let mut header = line.split(":");
    let mut key = String::from("");
    let mut value = String::from("");
    if let Some(k) = header.next() {
        key = k.to_string();
    }
    if let Some(v) = header.next() {
        value = v.to_string();
    }

    (key, value)
}
impl From<String> for HttpRequest {
    fn from(req: String) -> HttpRequest {
        let mut parsed_method = Method::Uninitialized;
        let mut parsed_version = Version::V1_1;
        let mut parsed_resource = Resource::Path("".to_string());
        let mut parsed_headers = HashMap::new();
        let mut parsed_body = "".to_string();

        for line in req.lines() {
            if line.contains("HTTP") {
                // 处理 请求行
                let (method, resource, version) = pricess_req_line(line);
                parsed_method = method;
                parsed_resource = resource;
                parsed_version = version;
            } else if line.contains(":") {
                // 处理 头部
                let (key, value) = process_header_line(line);
                parsed_headers.insert(key, value);
            } else if line.len() == 0 {
                // 处理 空行
            } else {
                parsed_body = line.to_string();
            }
        }

        HttpRequest {
            method: parsed_method,
            version: parsed_version,
            resource: parsed_resource,
            headers: parsed_headers,
            msg_body: parsed_body,
        }
    }
}

// 测试
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_method_into() {
        let method: Method = "GET".into();
        assert_eq!(method, Method::Get);
    }

    #[test]
    fn test_version_into() {
        let version: Version = "HTTP/1.1".into();
        assert_eq!(version, Version::V1_1);
    }

    #[test]
    fn test_read_http() {
        let req_str = "GET / HTTP/1.1\r\nHost: localhost:3000\r\nUser-Agent: curl/7.71.1\r\nAccept: */*\r\n\r\n".to_string();
        let mut headers_expected = HashMap::new();
        headers_expected.insert("Host".to_string(), " localhost".to_string());
        headers_expected.insert("Accept".into(), " */*".into());
        headers_expected.insert("User-Agent".into(), " curl/7.71.1".into());

        let req: HttpRequest = req_str.into();
        assert_eq!(req.method, Method::Get);
        assert_eq!(req.version, Version::V1_1);
        assert_eq!(req.resource, Resource::Path("/".to_string()));
        assert_eq!(req.headers, headers_expected);
    }
}
